 /*******************************************************************************
  * Copyright (c) 2003, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.navigator.extensions;

 import java.util.Iterator ;
 import java.util.LinkedHashSet ;
 import java.util.Set ;

 import org.eclipse.core.runtime.Assert;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.ISafeRunnable;
 import org.eclipse.core.runtime.SafeRunner;
 import org.eclipse.jface.viewers.ILabelProvider;
 import org.eclipse.jface.viewers.ILabelProviderListener;
 import org.eclipse.jface.viewers.ITreeContentProvider;
 import org.eclipse.ui.IMemento;
 import org.eclipse.ui.internal.navigator.NavigatorContentService;
 import org.eclipse.ui.internal.navigator.NavigatorPlugin;
 import org.eclipse.ui.navigator.ICommonContentProvider;
 import org.eclipse.ui.navigator.ICommonLabelProvider;
 import org.eclipse.ui.navigator.IExtensionStateModel;
 import org.eclipse.ui.navigator.IMementoAware;
 import org.eclipse.ui.navigator.INavigatorContentDescriptor;
 import org.eclipse.ui.navigator.INavigatorContentExtension;

 /**
  * <p>
  * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
  * part of a work in progress. There is a guarantee neither that this API will
  * work nor that it will remain the same. Please do not use this API without
  * consulting with the Platform/UI team.
  * </p>
  *
  * @since 3.2
  */
 public class NavigatorContentExtension implements IMementoAware,
         INavigatorContentExtension {

     private static final NavigatorContentExtension[] NO_EXTENSIONS = new NavigatorContentExtension[0];

     private NavigatorContentService contentService;

     private NavigatorContentDescriptor descriptor;
  
     private ICommonContentProvider contentProvider;

     private ICommonLabelProvider labelProvider;

     private boolean labelProviderInitializationFailed = false;

     private boolean contentProviderInitializationFailed = false;

     private boolean isDisposed = false;

     private IMemento appliedMemento;

     private StructuredViewerManager viewerManager;

     /**
      * Create an object to manage the instantiated elements from the extension.
      *
      * @param aDescriptor
      * The descriptor that knows how to create elements and knows the
      * id of the extension
      * @param aContentService
      * The content service that will manage this extension
      * @param aViewerManager
      * The viewer manager that knows how to initialize the content
      * provider created by this extension.
      */
     public NavigatorContentExtension(NavigatorContentDescriptor aDescriptor,
             NavigatorContentService aContentService,
             StructuredViewerManager aViewerManager) {
         super();
         Assert.isNotNull(aDescriptor);

         descriptor = aDescriptor;
         contentService = aContentService;
         viewerManager = aViewerManager;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.navigator.extensions.INavigatorContentExtension#getId()
      */
     public String getId() {
         return descriptor.getId();
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.navigator.extensions.INavigatorContentExtension#getDescriptor()
      */
     public INavigatorContentDescriptor getDescriptor() {
         return descriptor;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.navigator.INavigatorContentExtension#getContentProvider()
      */
     public ITreeContentProvider getContentProvider() {

         ITreeContentProvider provider = internalGetContentProvider();
         if (provider != SkeletonTreeContentProvider.INSTANCE) {
             return ((SafeDelegateTreeContentProvider) provider)
                     .getDelegateContentProvider();
         }
         return provider;
     }

     /**
      *
      * @return The internal content provider that is wrapped by this extension.
      */
     public ITreeContentProvider internalGetContentProvider() {
         if (contentProvider != null || contentProviderInitializationFailed) {
             return contentProvider;
         }
         synchronized (this) {
             try {
                 if (contentProvider == null) {
                     ITreeContentProvider treeContentProvider = descriptor
                             .createContentProvider();
                     if (treeContentProvider != null) {
                         contentProvider = new SafeDelegateTreeContentProvider(
                                 treeContentProvider, descriptor, contentService);
                         contentProvider.init(new CommonContentExtensionSite(
                                 getId(), contentService, appliedMemento));
                         viewerManager.initialize(contentProvider);
                     } else {
                         contentProvider = SkeletonTreeContentProvider.INSTANCE;
                     }
                 }
             } catch (CoreException e) {
                 contentProviderInitializationFailed = true;
                 e.printStackTrace();
             } catch (RuntimeException e) {
                 contentProviderInitializationFailed = true;
                 e.printStackTrace();
             }
             if (contentProviderInitializationFailed) {
                 contentProvider = SkeletonTreeContentProvider.INSTANCE;
             }
         }
         return contentProvider;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.navigator.extensions.INavigatorContentExtension#getLabelProvider()
      */
     public ICommonLabelProvider getLabelProvider() {
         if (labelProvider != null || labelProviderInitializationFailed) {
             return labelProvider;
         }
         synchronized (this) {
             try {

                 if (labelProvider == null) {
                     ILabelProvider tempLabelProvider = descriptor
                             .createLabelProvider();

                     if (tempLabelProvider instanceof ICommonLabelProvider) {
                         labelProvider = (ICommonLabelProvider) tempLabelProvider;
                             labelProvider.init(new CommonContentExtensionSite(
                                     getId(), contentService, appliedMemento));
                     } else {
                         labelProvider = new SafeDelegateCommonLabelProvider(
                                 tempLabelProvider);
                     }
                     
                     labelProvider.addListener( (ILabelProviderListener)contentService.createCommonLabelProvider() );
                 }
             } catch (CoreException e) {
                 labelProviderInitializationFailed = true;
                 e.printStackTrace();
             } catch (RuntimeException e) {
                 labelProviderInitializationFailed = true;
                 e.printStackTrace();
             }

             if (labelProviderInitializationFailed) {
                 labelProvider = SkeletonLabelProvider.INSTANCE;
             }
         }
         return labelProvider;
     }

     /**
      * Dispose of any resources acquired during the lifecycle of the extension.
      *
      */
     public void dispose() {
         try {
             synchronized (this) {
                     
                 SafeRunner.run(new ISafeRunnable() {

                     public void handleException(Throwable exception) {
                         String msg = exception.getMessage() != null ? exception.getMessage() : exception.toString() ;
                         NavigatorPlugin.logError(0, msg, exception);
                         
                     }

                     public void run() throws Exception {
                         if (contentProvider != null) {
                             contentProvider.dispose();
                         }

                     }
                     
                 });
                 
                 SafeRunner.run(new ISafeRunnable() {

                     public void handleException(Throwable exception) {
                         String msg = exception.getMessage() != null ? exception.getMessage() : exception.toString() ;
                         NavigatorPlugin.logError(0, msg, exception);
                         
                     }

                     public void run() throws Exception {
                         if (labelProvider != null) {
                             labelProvider.removeListener((ILabelProviderListener)contentService.createCommonLabelProvider());
                             labelProvider.dispose();
                         }

                     }
                     
                 });
     
             }
         } finally {
             isDisposed = true;
         }
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
      */
     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.navigator.extensions.INavigatorContentExtension#getAdapter(java.lang.Class)
      */
     public Object getAdapter(Class adapter) {
         return null;
     }

     /**
      * @return Returns the contentProviderInitializationFailed.
      */
     public boolean hasContentProviderInitializationFailed() {
         return contentProviderInitializationFailed;
     }

     /**
      * @return Returns the labelProviderInitializationFailed.
      */
     public boolean hasLabelProviderInitializationFailed() {
         return labelProviderInitializationFailed;
     }

     /**
      *
      * @return True if the loading of the content provider has failed.
      */
     public boolean hasLoadingFailed() {
         return contentProviderInitializationFailed;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.navigator.extensions.INavigatorContentExtension#isLoaded()
      */
     public boolean isLoaded() {
         return contentProvider != null;
     }

     public void restoreState(IMemento aMemento) {
         synchronized (this) {
             appliedMemento = aMemento;
             applyMemento(contentProvider);
             applyMemento(labelProvider);

         }
     }

     public void saveState(IMemento aMemento) {
         synchronized (this) {
             if(contentProvider != null && contentProvider instanceof IMementoAware)
                 ((IMementoAware)contentProvider).saveState(aMemento);
             if(labelProvider != null && labelProvider instanceof IMementoAware)
                 ((IMementoAware)labelProvider).saveState(aMemento);
             
         }
     }

     private void applyMemento(IMementoAware target) {
         if (target != null) {
             target.restoreState(appliedMemento);
         }

     }

     protected final void complainDisposedIfNecessary() {
         if (isDisposed) {
             throw new IllegalStateException ("INavigatorContentExtension " //$NON-NLS-1$
 + descriptor.getId() + " is disposed!"); //$NON-NLS-1$
 }
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.navigator.extensions.INavigatorContentExtension#getStateModel()
      */
     public IExtensionStateModel getStateModel() {
         return contentService.getExtensionStateService().getExtensionStateModel(getDescriptor());
     }

     /**
      * @param anElement
      * The element for the query.
      * @return Returns the overridingExtensions.
      */
     public NavigatorContentExtension[] getOverridingExtensionsForTriggerPoint(
             Object anElement) {
         if (!descriptor.hasOverridingExtensions()) {
             return NO_EXTENSIONS;
         }

         NavigatorContentDescriptor overriddingDescriptor;
         Set overridingExtensions = new LinkedHashSet ();
         for (Iterator contentDescriptorsItr = descriptor
                 .getOverriddingExtensions().iterator(); contentDescriptorsItr
                 .hasNext();) {
             overriddingDescriptor = (NavigatorContentDescriptor) contentDescriptorsItr
                     .next();

             if (contentService.isActive(overriddingDescriptor.getId())
                     && contentService.isVisible(overriddingDescriptor.getId())
                     && overriddingDescriptor.isTriggerPoint(anElement)) {
                 overridingExtensions.add(contentService
                         .getExtension(overriddingDescriptor));
             }
         }
         if (overridingExtensions.size() == 0) {
             return NO_EXTENSIONS;
         }
         return (NavigatorContentExtension[]) overridingExtensions
                 .toArray(new NavigatorContentExtension[overridingExtensions
                         .size()]);
     }

     /**
      *
      * @param anElement
      * The element for the query.
      * @return Returns the overridingExtensions.
      */
     public NavigatorContentExtension[] getOverridingExtensionsForPossibleChild(
             Object anElement) {
         if (!descriptor.hasOverridingExtensions()) {
             return NO_EXTENSIONS;
         }

         NavigatorContentDescriptor overriddingDescriptor;
         Set overridingExtensions = new LinkedHashSet ();
         for (Iterator contentDescriptorsItr = descriptor
                 .getOverriddingExtensions().iterator(); contentDescriptorsItr
                 .hasNext();) {
             overriddingDescriptor = (NavigatorContentDescriptor) contentDescriptorsItr
                     .next();

             if (contentService.isActive(overriddingDescriptor.getId())
                     && contentService.isVisible(overriddingDescriptor.getId())
                     && overriddingDescriptor.isPossibleChild(anElement)) {
                 overridingExtensions.add(contentService
                         .getExtension(overriddingDescriptor));
             }
         }
         if (overridingExtensions.size() == 0) {
             return NO_EXTENSIONS;
         }
         return (NavigatorContentExtension[]) overridingExtensions
                 .toArray(new NavigatorContentExtension[overridingExtensions
                         .size()]);
     }

     /*
      * (non-Javadoc)
      *
      * @see java.lang.Object#toString()
      */
     public String toString() {
         return descriptor.toString() + " Instance"; //$NON-NLS-1$
 }
 }

